import atexit
import logging
import os
import sys
import threading
from logging.handlers import QueueHandler, QueueListener
from queue import Queue
from typing import Dict, Literal, Optional

from concurrent_log_handler import (
    ConcurrentRotatingFileHandler,
    ConcurrentTimedRotatingFileHandler,
)

# 轮转类型
RotationType = Literal["size", "time"]


class AsyncSafeLogger:
    """
    支持按大小或按时间（每日）轮转的日志管理器。
    """

    def __init__(
        self,
        log_level: int = logging.INFO,
        log_file: str = "logs/app.log",
        rotation_type: RotationType = "time",  # "time" 或 "size"
        # 按大小轮转参数
        max_bytes: int = 10 * 1024 * 1024,  # 10 MB
        backup_count: int = 5,
        # 按时间轮转参数（每日）
        when: str = "midnight",  # 'S', 'M', 'H', 'D', 'midnight'
        interval: int = 1,
        encoding: str = "utf-8",
        delay: bool = False,
        console: bool = True,
        dependency_levels: Optional[Dict[str, int]] = None,
        formatter: Optional[logging.Formatter] = None,
        console_formatter: Optional[logging.Formatter] = None,
    ):
        self.log_level = log_level
        self.log_file = log_file
        self.rotation_type = rotation_type
        self.max_bytes = max_bytes
        self.backup_count = backup_count
        self.when = when
        self.interval = interval
        self.encoding = encoding
        self.delay = delay
        self.console = console
        self.dependency_levels = dependency_levels or {}
        self.formatter = formatter or logging.Formatter(
            "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d - %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
        )
        self.console_formatter = console_formatter or self.formatter
        self._queue = None
        self._listener = None
        self._initialized = False
        self._lock = threading.Lock()

    def _create_handler(self):
        """根据轮转类型创建 handler"""
        if self.rotation_type == "size":
            handler = ConcurrentRotatingFileHandler(
                filename=self.log_file,
                maxBytes=self.max_bytes,
                backupCount=self.backup_count,
                encoding=self.encoding,
                delay=self.delay,
            )
        elif self.rotation_type == "time":
            # 按天轮转：每天生成新文件，如 app-2024-06-02.log
            handler = ConcurrentTimedRotatingFileHandler(
                filename=self.log_file,
                when=self.when,  # 'midnight' 表示每天午夜轮转
                interval=self.interval,
                backupCount=self.backup_count,  # 保留最近 N 个文件
                encoding=self.encoding,
                delay=self.delay,
                utc=False,  # 使用本地时间
            )
            # 自定义文件后缀格式（默认是 .2024-06-02，我们希望是 -2024-06-02.log）
            handler.suffix = "%Y-%m-%d"
            handler.extMatch = r"^\d{4}-\d{2}-\d{2}$"
        else:
            raise ValueError(f"Unsupported rotation_type: {self.rotation_type}")

        handler.setFormatter(self.formatter)
        return handler

    def setup(self):
        if self._initialized:
            return

        with self._lock:
            if self._initialized:
                return

            os.makedirs(os.path.dirname(self.log_file), exist_ok=True)
            self._queue = Queue()

            handlers = []
            # 文件 handler
            file_handler = self._create_handler()
            handlers.append(file_handler)

            # 控制台
            if self.console:
                console_handler = logging.StreamHandler(sys.stdout)
                console_handler.setFormatter(self.console_formatter)
                handlers.append(console_handler)

            self._listener = QueueListener(self._queue, *handlers, respect_handler_level=True)
            self._listener.start()
            atexit.register(self._listener.stop)

            # 配置 root logger
            root_logger = logging.getLogger()
            root_logger.handlers.clear()
            root_logger.addHandler(QueueHandler(self._queue))
            root_logger.setLevel(self.log_level)

            # 设置依赖级别
            for name, level in self.dependency_levels.items():
                logging.getLogger(name).setLevel(level)

            self._initialized = True

    def get_logger(self, name: str) -> logging.Logger:
        if not self._initialized:
            self.setup()
        return logging.getLogger(name)

    def shutdown(self):
        if self._listener:
            self._listener.stop()
            self._listener = None
        self._initialized = False


def parse_log_level(level_str: str) -> int:
    """将日志级别字符串转换为logging模块的常量

    Args:
        level_str: 日志级别字符串

    Returns:
        int: logging模块的级别常量

    Raises:
        ValueError: 当传入无效的日志级别字符串时
    """
    level_mapping = {
        "DEBUG": logging.DEBUG,
        "INFO": logging.INFO,
        "WARNING": logging.WARNING,
        "ERROR": logging.ERROR,
        "CRITICAL": logging.CRITICAL,
    }

    upper_level = level_str.upper()
    if upper_level not in level_mapping:
        raise ValueError(f"Invalid log level: {level_str}. " f"Must be one of: {list(level_mapping.keys())}")

    return level_mapping[upper_level]
